"""
活动数据验证工具
提供活动创建和更新时的数据验证功能
"""
import re
from datetime import datetime, timedelta
from app.models.user import User
from app.models.venue import Venue

class ActivityValidator:
    """活动数据验证器"""
    
    # 有效的活动状态
    VALID_STATUSES = ['open', 'closed', 'cancelled']
    
    # 活动标题最大长度
    MAX_TITLE_LENGTH = 100
    
    # 活动描述最大长度
    MAX_DESCRIPTION_LENGTH = 500
    
    # 最大参与人数限制
    MAX_PARTICIPANTS_LIMIT = 1000
    
    @classmethod
    def validate_activity_data(cls, data, is_update=False):
        """
        验证活动数据
        
        Args:
            data: 要验证的数据字典
            is_update: 是否为更新操作
            
        Raises:
            ValueError: 当数据验证失败时
        """
        if not data or not isinstance(data, dict):
            raise ValueError("请求数据不能为空")
            
        errors = []
        
        # 基础字段验证
        if not is_update or 'title' in data:
            title = data.get('title', '').strip()
            if not title:
                errors.append('活动标题不能为空')
            elif len(title) > cls.MAX_TITLE_LENGTH:
                errors.append(f'活动标题长度不能超过{cls.MAX_TITLE_LENGTH}个字符')
        
        if not is_update or 'description' in data:
            description = data.get('description', '')
            if description and len(description) > cls.MAX_DESCRIPTION_LENGTH:
                errors.append(f'活动描述长度不能超过{cls.MAX_DESCRIPTION_LENGTH}个字符')
        
        # 组织者验证
        if not is_update or 'organizer_id' in data:
            organizer_id = data.get('organizer_id')
            if not organizer_id:
                errors.append('组织者ID不能为空')
            elif not isinstance(organizer_id, int) or organizer_id <= 0:
                errors.append('组织者ID必须为正整数')
            else:
                # 检查组织者是否存在
                if not User.query.get(organizer_id):
                    errors.append(f'组织者ID {organizer_id} 不存在')
        
        # 场馆验证（可选）
        if 'venue_id' in data:
            venue_id = data.get('venue_id')
            if venue_id is not None:
                if not isinstance(venue_id, int) or venue_id <= 0:
                    errors.append('场馆ID必须为正整数')
                else:
                    # 检查场馆是否存在
                    if not Venue.query.get(venue_id):
                        errors.append(f'场馆ID {venue_id} 不存在')
        
        # 最大参与人数验证
        if 'max_participants' in data:
            max_participants = data.get('max_participants')
            if max_participants is not None:
                if not isinstance(max_participants, int) or max_participants <= 0:
                    errors.append('最大参与人数必须为正整数')
                elif max_participants > cls.MAX_PARTICIPANTS_LIMIT:
                    errors.append(f'最大参与人数不能超过{cls.MAX_PARTICIPANTS_LIMIT}人')
        
        # 状态验证
        if 'status' in data:
            status = data.get('status')
            if status and status not in cls.VALID_STATUSES:
                errors.append(f'无效的活动状态，支持的状态：{", ".join(cls.VALID_STATUSES)}')
        
        # 图片验证
        if 'images' in data:
            images = data.get('images')
            if images is not None:
                if not isinstance(images, list):
                    errors.append('活动图片必须为数组格式')
                elif len(images) > 10:
                    errors.append('活动图片最多10张')
                else:
                    for i, image in enumerate(images):
                        if not isinstance(image, str) or not image.strip():
                            errors.append(f'第{i+1}张图片URL不能为空')
        
        if errors:
            raise ValueError('; '.join(errors))
    
    @classmethod
    def validate_time_data(cls, data, is_update=False):
        """
        验证时间相关数据
        
        Args:
            data: 包含时间字段的数据字典
            is_update: 是否为更新操作
            
        Raises:
            ValueError: 当时间验证失败时
        """
        errors = []
        
        start_time = None
        end_time = None
        venue_start_time = None
        venue_end_time = None
        
        # 解析和验证开始时间
        if not is_update or 'start_time' in data:
            start_time_str = data.get('start_time')
            if not start_time_str:
                errors.append('活动开始时间不能为空')
            else:
                try:
                    start_time = datetime.strptime(start_time_str, '%Y-%m-%d %H:%M:%S')
                    # 验证开始时间不能是过去时间
                    if start_time <= datetime.now():
                        errors.append('活动开始时间必须晚于当前时间')
                except ValueError:
                    errors.append('开始时间格式错误，请使用 YYYY-MM-DD HH:MM:SS 格式')
        
        # 解析和验证结束时间
        if not is_update or 'end_time' in data:
            end_time_str = data.get('end_time')
            if not end_time_str:
                errors.append('活动结束时间不能为空')
            else:
                try:
                    end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S')
                except ValueError:
                    errors.append('结束时间格式错误，请使用 YYYY-MM-DD HH:MM:SS 格式')
        
        # 验证时间逻辑关系
        if start_time and end_time:
            if start_time >= end_time:
                errors.append('活动开始时间必须早于结束时间')
            
            # 验证活动时长合理性（不能超过24小时）
            duration = end_time - start_time
            if duration.total_seconds() > 24 * 3600:
                errors.append('活动时长不能超过24小时')
            elif duration.total_seconds() < 15 * 60:
                errors.append('活动时长不能少于15分钟')
        
        # 验证场馆预订时间（可选）
        if 'venue_start_time' in data:
            venue_start_time_str = data.get('venue_start_time')
            if venue_start_time_str:
                try:
                    venue_start_time = datetime.strptime(venue_start_time_str, '%Y-%m-%d %H:%M:%S')
                except ValueError:
                    errors.append('场馆预订开始时间格式错误')
        
        if 'venue_end_time' in data:
            venue_end_time_str = data.get('venue_end_time')
            if venue_end_time_str:
                try:
                    venue_end_time = datetime.strptime(venue_end_time_str, '%Y-%m-%d %H:%M:%S')
                except ValueError:
                    errors.append('场馆预订结束时间格式错误')
        
        # 验证场馆预订时间逻辑
        if venue_start_time and venue_end_time:
            if venue_start_time >= venue_end_time:
                errors.append('场馆预订开始时间必须早于结束时间')
            
            # 验证场馆预订时间应该包含活动时间
            if start_time and venue_start_time > start_time:
                errors.append('场馆预订开始时间不能晚于活动开始时间')
            
            if end_time and venue_end_time < end_time:
                errors.append('场馆预订结束时间不能早于活动结束时间')
        
        if errors:
            raise ValueError('; '.join(errors))
        
        return {
            'start_time': start_time,
            'end_time': end_time,
            'venue_start_time': venue_start_time,
            'venue_end_time': venue_end_time
        }
    
    @classmethod
    def validate_venue_capacity(cls, venue_id, max_participants):
        """
        验证场馆容量限制
        
        Args:
            venue_id: 场馆ID
            max_participants: 最大参与人数
            
        Raises:
            ValueError: 当容量验证失败时
        """
        if venue_id and max_participants:
            venue = Venue.query.get(venue_id)
            if venue and venue.max_capacity:
                if max_participants > venue.max_capacity:
                    raise ValueError(f'最大参与人数不能超过场馆容量（{venue.max_capacity}人）')
    
    @classmethod
    def validate_venue_time_conflict(cls, venue_id, venue_start_time, venue_end_time, activity_id=None):
        """
        验证场馆时间冲突
        
        Args:
            venue_id: 场馆ID
            venue_start_time: 场馆预订开始时间
            venue_end_time: 场馆预订结束时间
            activity_id: 活动ID（更新时排除自己）
            
        Raises:
            ValueError: 当存在时间冲突时
        """
        if not venue_id or not venue_start_time or not venue_end_time:
            return
        
        from app.models.activity import Activity
        from app import db
        
        # 构建查询，排除已取消的活动
        query = Activity.query.filter(
            Activity.venue_id == venue_id,
            Activity.status.in_(['open', 'closed']),
            db.or_(
                # 活动开始时间在查询时间段内
                db.and_(Activity.venue_start_time >= venue_start_time, Activity.venue_start_time < venue_end_time),
                # 活动结束时间在查询时间段内
                db.and_(Activity.venue_end_time > venue_start_time, Activity.venue_end_time <= venue_end_time),
                # 活动时间段包含查询时间段
                db.and_(Activity.venue_start_time <= venue_start_time, Activity.venue_end_time >= venue_end_time)
            )
        )
        
        # 更新时排除自己
        if activity_id:
            query = query.filter(Activity.id != activity_id)
        
        conflicting_activity = query.first()
        if conflicting_activity:
            raise ValueError(f'场馆在该时间段已被活动"{conflicting_activity.title}"占用')
    
    @classmethod
    def validate_search_params(cls, page, per_page, keyword):
        """
        验证搜索参数
        
        Args:
            page: 页码
            per_page: 每页数量
            keyword: 搜索关键词
            
        Raises:
            ValueError: 当参数验证失败时
        """
        errors = []
        
        # 页码验证
        if page is not None:
            if not isinstance(page, int) or page < 1:
                errors.append('页码必须为正整数')
            elif page > 10000:
                errors.append('页码不能超过10000')
        
        # 每页数量验证
        if per_page is not None:
            if not isinstance(per_page, int) or per_page < 1:
                errors.append('每页数量必须为正整数')
            elif per_page > 100:
                errors.append('每页数量不能超过100')
        
        # 关键词验证
        if keyword is not None:
            if len(keyword) > 50:
                errors.append('搜索关键词长度不能超过50个字符')
            # 检查是否包含特殊字符（防止注入）
            if re.search(r'[<>"\';]', keyword):
                errors.append('搜索关键词包含非法字符')
        
        if errors:
            raise ValueError('; '.join(errors))
    
    @classmethod
    def validate_statistics_params(cls, start_date, end_date):
        """
        验证统计参数
        
        Args:
            start_date: 开始日期字符串
            end_date: 结束日期字符串
            
        Raises:
            ValueError: 当参数验证失败时
        """
        errors = []
        parsed_start = None
        parsed_end = None
        
        # 解析开始日期
        if start_date:
            try:
                parsed_start = datetime.strptime(start_date, '%Y-%m-%d')
            except ValueError:
                errors.append('开始日期格式错误，请使用 YYYY-MM-DD 格式')
        
        # 解析结束日期
        if end_date:
            try:
                parsed_end = datetime.strptime(end_date, '%Y-%m-%d')
            except ValueError:
                errors.append('结束日期格式错误，请使用 YYYY-MM-DD 格式')
        
        # 验证日期逻辑
        if parsed_start and parsed_end:
            if parsed_start > parsed_end:
                errors.append('开始日期不能晚于结束日期')
            
            # 验证时间范围不能超过1年
            if (parsed_end - parsed_start).days > 365:
                errors.append('统计时间范围不能超过1年')
        
        # 验证日期不能是未来日期
        today = datetime.now().date()
        if parsed_start and parsed_start.date() > today:
            errors.append('开始日期不能是未来日期')
        
        if parsed_end and parsed_end.date() > today:
            errors.append('结束日期不能是未来日期')
        
        if errors:
            raise ValueError('; '.join(errors))
        
        return parsed_start, parsed_end 